package com.sword.invoice.channel.lq.infrastructure;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.github.rholder.retry.*;
import com.sword.invoice.channel.lq.bo.BussinessResponse;
import com.sword.invoice.channel.lq.bo.LqBussinessHeader;
import com.sword.invoice.channel.lq.bo.ResponseError;
import com.sword.invoice.channel.lq.constant.ResponseConstant;
import com.sword.invoice.channel.lq.exception.RetryConnectException;
import com.sword.invoice.channel.lq.utils.LqHttpClientHelper;
import com.sword.invoice.channel.lq.utils.SM4Util;

import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;


/**
 * SDK抽象方法类，将方法隔离
 * 1、接口较多，隔离目的为提高可读性
 * 2、统一协议通讯方法
 * 2、定义通用方法步骤，具体校验和报文组装逻辑各自实现
 * 3、防止版本更新出问题相互影响，减少问题排查时间
 * @Author zhangshiheng
 * @Date 2022-04-21 9:53
 * @Version 1.0
 **/
public abstract class AbstractConverter<T> {

    //接口标识，由各实现类初始化
/*    public  String  actionId;
    public  String  actionName;
    public  String  nlbm;
    public  String  ylbm;*/
    //调用
    public static BussinessResponse invoke(String url, LqBussinessHeader headerParams , Object params){

        //因为乐企开票不同的能力，存在相同的服务接口，但能力编码和用例编码不一样，存在公共的服务接口统一用乐企（基础）能力的能力编码和用例编码
        //对于沙箱测试场景，则需要传对应开票能力的能力编码和用例编码，那么对象需要set值。
        //对于生产环境，对象不指定值，取默认能力编码和用例编码，用于生产环境
/*        if(headerParams.getNlbm()==null||headerParams.getNlbm().equals("")){
            headerParams.setNlbm(nlbm);
        }
        if(headerParams.getYlbm()==null||headerParams.getYlbm().equals("")){
            headerParams.setYlbm(ylbm);
        }
        if(headerParams.getFwbm()==null||headerParams.getFwbm().equals("")){
            headerParams.setFwbm(actionId);
        }
        if(headerParams.getFwbm_desc()==null||headerParams.getFwbm_desc().equals("")){
            headerParams.setFwbm_desc(actionName);
        }*/

//        log.info("乐企引擎全电发票接口调用:*********"+headerParams.getFwbm_desc()+"("+headerParams.getFwbm()+")，能力编码"+headerParams.getNlbm()+",用例编码"+headerParams.getYlbm()+"*********");

        BussinessResponse bussinessResponse=null;

        try {
            String requestPlain = JSON.toJSONString(params);
            //最终请求参数转换json字符串
//            String requestPlain =converterRequest(params);

            String senid= UUID.randomUUID().toString().replace("-", "");
       /*     log.info("------------------请求参数报文-"+senid+"-----------------");
            log.info(requestPlain);*/
            //SM4加密
            String requestCipher = SM4Util.encryptEcb(headerParams.getSm4_key(),requestPlain);
//            log.debug(requestCipher);
            //设置服务编码
            headerParams.setFwbm(headerParams.getFwbm());

            //请求执行体
            Callable<BussinessResponse> httpRequestCallable = () -> {
                BussinessResponse httpResponse=null;
                //拼接请求地址
                String requestUrl=url+"/"+headerParams.getNlbm()+"/"+headerParams.getFwbm();
                //调用税局端，取得报文结果
//                log.info(requestUrl);
                try{
                    String response= LqHttpClientHelper.doJsonSSLPost(requestUrl,headerParams,requestCipher);
                  /*  log.info("------------------响应报文-"+senid+"----------------");
                    log.info(response);*/
                    //转换响应结果
                    if(response!=null) {
//                        log.info("------------------解密响应报文-"+senid+"----------------");
                        //解密data节点报文
                        response=decodeResponse(response,headerParams.getSm4_key());
//                        log.info(response);
                        //转换对象结构
                        httpResponse = JSON.parseObject(response, BussinessResponse.class);
//                        httpResponse = converterEntity(response);

                    }else{
                        httpResponse =  errorResponse("HX9999", "乐企响应报文为空");
                    }
                } catch (Exception e) {
                    httpResponse = errorResponse("HX9999", e.getMessage());
                }

                return httpResponse;
            };

            //构建Retryer重试机制
            //由于税局端不稳定，总共分为两大类异常：
            // 1、技术协议抛的异常（连接超时、连接断开），是税局接口端状态异常产生
            // 2、报文内返回技术异常（返回码500），是税局接口端与税局后端服务通讯或状态异常产生，将税局内部应用处理异常抛了给我们对接方
            //捕获后统一抛出RetryConnectException，触发3次重试
            Retryer<BussinessResponse> retryer = RetryerBuilder.<BussinessResponse>newBuilder()
                    // 设置重试条件
                    .retryIfExceptionOfType(RetryConnectException.class)
                    // 设置停止策略：最多重试3次
                    .withStopStrategy(StopStrategies.stopAfterAttempt(3))
                    // 设置等待策略：每次重试间隔2秒
                    .withWaitStrategy(WaitStrategies.fixedWait(2, TimeUnit.SECONDS))
                    // 添加重试监听器
                    .withRetryListener(new LqRetryListener())
                    .build();


            return retryer.call(httpRequestCallable);

        } catch (RetryException e){
            bussinessResponse = errorResponse("HX9999", "税局端状态异常，已重试3次，请稍后再试");
        }catch (Exception e) {
            bussinessResponse = errorResponse("HX9999",  "未知错误："+e.getMessage());
            e.printStackTrace();
        }

        return bussinessResponse;
    }


    //返回结果转换实体类对象，各自实现
//    protected abstract BussinessResponse converterEntity(String response);


    //返回自定义错误对象
    protected static BussinessResponse errorResponse(String code,String message){
        BussinessResponse bussinessResponse=new BussinessResponse();
        ResponseError error=new ResponseError();
        error.setCode(code);
        error.setMessage(message);
        bussinessResponse.setError(error);
        return bussinessResponse;
    }

    //响应信息解码
    protected static String decodeResponse(String jsonString,String key) throws Exception {

        String result=null;
        String responsePlain=null;
        //第1层根节点
        JSONObject rootNode=JSONObject.parseObject(jsonString);
        String httpStatusCode=rootNode.getString("httpStatusCode");
        if(httpStatusCode!=null){
            if(httpStatusCode.equals(ResponseConstant.REQUEST_SUCCESS.getCode())){
                //获取第2层body
                JSONObject bodyNode=JSONObject.parseObject(rootNode.getString("body"));
                //获取第2层Response
                JSONObject responseNode=JSONObject.parseObject(bodyNode.getString("Response"));
                //获取第4层Data
                String responseChipher=responseNode.getString("Data");
                //编码后转换新的json结构
                JSONObject dataNode=null;
                if(responseChipher!=null&&!responseChipher.equals("")){
                    //有data节点才解密
                    responsePlain= SM4Util.decryptEcb(key,responseChipher);
                    dataNode=JSONObject.parseObject(responsePlain);
                    responseNode.put("Data",dataNode);
                }
                bodyNode.put("Response",responseNode);
                result= bodyNode.toJSONString();
            }else{
//                throw new RetryConnectException(httpStatusCode,"乐企服务响应异常："+rootNode.getString("body"));
            }
        }else {
//            throw new RetryConnectException("99","乐企请求返回状态码为空");
        }


        return result;
    }

    //重试监听
    private static class LqRetryListener implements RetryListener {
        @Override
        public void onRetry(Attempt attempt) {
            if(attempt.getAttemptNumber()>1){
//                log.info("完成第 "+(attempt.getAttemptNumber()-1)+" 次重试请求乐企服务");
            }
        }
    }
}
